/*
** Copyright (C) 2010 Calin Radoni
**
** This file is part of CHScanner.
**
** CHScanner is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CHScanner is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CHScanner. If not, see <http://www.gnu.org/licenses/>.
*/

#include "StdAfx.h"
#include ".\rgnwindow.h"

//---------------------------------------------------------------------------

RgnWindow::RgnWindow(void)
{
	currentRecType = __rwrtNone;
	currentPoly = NULL;
}

RgnWindow::~RgnWindow(void)
{
	CleanUp();
}

//---------------------------------------------------------------------------

void RgnWindow::CleanUp(void)
{
	MListElement *elem;
	RgnPolygon *poly;

	elem = polygons.GetHead();
	while(elem!=NULL){
		poly = (RgnPolygon*)elem->Data.Data;
		if(poly!=NULL){
			elem->Data.Data = NULL;
			delete poly;
		}
		elem = elem->Next;
	}
	polygons.Clear();
}

//---------------------------------------------------------------------------

void RgnWindow::Initialize(void)
{
	CleanUp();

	currentRecType = __rwrtNone;
	currentPoly = NULL;
}

//---------------------------------------------------------------------------

HRGN RgnWindow::CreateNewRegion(RgnPolygon* poly, int wIn, int hIn)
{
	POINT* points;
	int idx, cnt;
	HRGN rgn;

	if(poly==NULL) return NULL;

	// check if the polygon have enough vertexes
	cnt = poly->GetCount();
	if(cnt<3) return NULL;

	// create an array of POINT objects
	try{
		points = new POINT[cnt];
	}
	catch(...){
		points = NULL;
	}
	if(points==NULL) return NULL;

	// fill the array of POINTs
	for(idx=0; idx<cnt; idx++){
		points[idx].x = poly->GetX(idx, wIn);
		points[idx].y = poly->GetY(idx, hIn);
	}

	// create the region
	rgn = CreatePolygonRgn(points, cnt, WINDING);

	// destory the array of POINT objects
	delete[] points;

	return rgn;
}

bool RgnWindow::IsRegionPolygon(RgnPolygon *poly)
{
	RgnPloygonType polyType;

	if(poly==NULL) return false;

	polyType = poly->GetType();
	if(polyType!=__rptPolygon)
		return false;

	return true;
}

bool RgnWindow::SetRegion(HWND hWnd, int wIn, int hIn)
{
	RgnPolygon *poly;
	int idx, cnt;

	HRGN rgnMain;
	HRGN rgnChild;
	int res;

	if(hWnd==NULL) return false;

	rgnMain = NULL;

	cnt = polygons.GetCount();
	for(idx=0; idx<cnt; idx++){
		// process each polygon
		poly = (RgnPolygon*)polygons.GetItem(idx);
		if(IsRegionPolygon(poly)){
			rgnChild = CreateNewRegion(poly, wIn, hIn);
			if(rgnChild!=NULL){
				if(rgnMain==NULL){
					rgnMain = rgnChild;
					rgnChild = NULL;
				}
				else{
					// combine the new region with the first one
					res = CombineRgn(rgnMain, rgnMain, rgnChild, RGN_OR);
					if(res==NULLREGION || res==ERROR){
						// fucking error - create a rectangle region
						rgnMain = CreateRectRgn(0, 0, wIn, hIn);
					}
					// 'rgnChild' is not needed anymore
					DeleteObject(rgnChild);
					rgnChild = NULL;
				}
			}
		}
	}

	if(rgnMain==NULL){
		// region was NOT created, create a default one
		rgnMain = CreateRectRgn(0, 0, wIn, hIn);
	}

	if(rgnMain==NULL){
		// no region could be created, definitelly returns false
		return false;
	}

	res = SetWindowRgn(hWnd, rgnMain, TRUE);
	if(res==0){
		// the window region could not be set
		DeleteObject(rgnMain);
		return false;
	}

	return true;
}

//---------------------------------------------------------------------------

bool RgnWindow::GetClientRectangle(RECT* rectIn, int wwIn, int hhIn)
{
	RgnPolygon *poly;
	int i, cnt;
	bool found;

	if(rectIn==NULL) return true;

	found = false;
	cnt = polygons.GetCount();
	for(i=0; i<cnt && !found; i++){
		// process each polygon
		poly = (RgnPolygon*)polygons.GetItem(i);
		if(poly!=NULL){
			if(poly->GetType()==__rptClient){
				if(poly->GetCount()!=2){
					// not a rectangular one, error
					return false;
				}
				else{
					rectIn->left    = poly->GetX(0, wwIn);
					rectIn->top     = poly->GetY(0, hhIn);
					rectIn->right   = poly->GetX(1, wwIn);
					rectIn->bottom  = poly->GetY(1, hhIn);
					found = true;
				}
			}
		}
	}

	return found;
}

//---------------------------------------------------------------------------

RgnPolygon* RgnWindow::CreateNewPolygon(RgnPloygonType typeIn)
{
	RgnPolygon* poly;

	try{
		poly = new RgnPolygon;
	}
	catch(...){
		poly = NULL;
	}
	if(poly==NULL) return NULL;

	poly->SetType(typeIn);

	return poly;
}

bool RgnWindow::AddCurrentPolygon(void)
{
	if(currentPoly==NULL){
		// nothing to add
		return true;
	}

	if(!polygons.AddLast(currentPoly)){
		// failed to add the polygon
		delete currentPoly;
		currentPoly = NULL;
		currentRecType = __rwrtNone;
		return false;
	}

	// added OK, prepare for next processing
	currentPoly = NULL;
	currentRecType = __rwrtNone;

	return true;
}

bool RgnWindow::ProcessLine(char* buf)
{
	CString str;
	int vA, vB, vC, vD;

	if(buf==NULL) return false;

	// new section ?
	if(buf[0]=='['){
		// add collected data
		if(!AddCurrentPolygon())
			return false;

		// and go on ...
		return ProcessRecTypeLine(buf);
	}

	if(currentRecType!=__rwrtPolygon && currentRecType!=__rwrtClient){
		// unknown record type, ignore
		return true;
	}

	if(buf[0]=='\n'){
		// just an empty line
		return true;
	}

	if(currentPoly==NULL)
		return false;

	// this should be a point

	str = buf;

	if(str.GetLength()!=20 && str.GetLength()!=21)
		return false;

	str = str.Left(5);
	str = str.Trim();
	vA = _tstoi(str);

	str = buf;
	str = str.Mid(5, 5);
	str = str.Trim();
	vB = _tstoi(str);

	str = buf;
	str = str.Mid(10, 5);
	str = str.Trim();
	vC = _tstoi(str);

	str = buf;
	str = str.Mid(15, 5);
	str = str.Trim();
	vD = _tstoi(str);

	if(!currentPoly->AddPoint(vA, vB, vC, vD))
		return false;

	return true;
}

bool RgnWindow::ProcessRecTypeLine(char* buf)
{
	RgnPolygon* newPoly;
	CString str;

	// this should not happend
	if(buf==NULL) return false;

	str = buf;
	str = str.Left(6);
	if(str.CompareNoCase("[Poly]")==0){
		currentRecType = __rwrtPolygon;
		newPoly = CreateNewPolygon(__rptPolygon);
		if(newPoly==NULL){
			currentRecType = __rwrtNone;
			return false;
		}

		currentPoly = newPoly;
		return true;
	}

	str = buf;
	str = str.Left(8);
	if(str.CompareNoCase("[Client]")==0){
		currentRecType = __rwrtClient;
		newPoly = CreateNewPolygon(__rptClient);
		if(newPoly==NULL){
			currentRecType = __rwrtNone;
			return false;
		}

		currentPoly = newPoly;
		return true;
	}

	currentRecType = __rwrtUnknown;
	return true;
}

bool RgnWindow::LoadFromFile(CString fileName)
{
	BOOL res;
	char buffer[1025];
	LPTSTR sres;
	bool status;
	
	res = theFile.Open(fileName, CFile::modeRead);
	if(!res) return false;

	CleanUp();

	currentRecType = __rwrtNone;
	currentPoly = NULL;

	status = true;
	sres = &buffer[0];
	while(sres!=NULL && status){
		memset(buffer, 0, 1025);
		sres = theFile.ReadString(buffer, 1024);
		if(sres!=NULL){
			if(buffer[0]=='#'){
				// comment line
			}
			else{
				// process the line
				status = ProcessLine(buffer);
			}
		}
	}

	if(status){
		// add the last readed data
		status = AddCurrentPolygon();
	}

	theFile.Close();

	return status;
}

//---------------------------------------------------------------------------

bool RgnWindow::SetDefaultClientRegion(int dd)
{
	RgnPolygon *poly;

	Initialize();

	try{
		poly = new RgnPolygon;
	}
	catch(...){
		poly = NULL;
	}
	if(poly==NULL) return false;

	poly->AddPoint(dd, 0, dd + 13, 0);
	poly->AddPoint(-1*dd, 1, -1*dd, 1);
	poly->SetType(__rptClient);

	if(!polygons.AddLast(poly)){
		delete poly;
		Initialize();
		return false;
	}

	return true;
}

//---------------------------------------------------------------------------
